﻿/************************************************************************************
 * xujiajun@yoozoo.com 御剑
 * 提供添加圆形碰撞的抽象类，所有添加进来的圆形将以中心坐标(x,y)为键值进行存储，泛型T为数据类型
 ***********************************************************************************/

using System.Collections.Generic;
using Unity.Mathematics;


namespace WorldMapBase.Collision
{
    /// <summary>
    /// 圆形碰撞数据抽象类
    /// </summary>
    public abstract class CircleCollisionData<T> : ICollisionData
    {
        /// <summary>
        /// 格子尺寸
        /// </summary>
        private int m_GridSize;

        /// <summary>
        /// 碰撞数据集合<x, <y, 数据>>
        /// </summary>
        private Dictionary<int, Dictionary<int, T>> m_CollisionDic;

        /// <summary>
        /// 防止新建局部列表所定义的列表
        /// </summary>
        private List<T> m_TmpList;

        public CircleCollisionData(int gridSize)
        {
            m_GridSize = gridSize;
            m_CollisionDic = new Dictionary<int, Dictionary<int, T>>();
            m_TmpList = new List<T>();
        }

        /// <summary>
        /// 以指定坐标为中心，半径所覆盖的区域添加指定碰撞
        /// </summary>
        /// <param name="id">碰撞id</param>
        /// <param name="x">横坐标</param>
        /// <param name="y">纵坐标</param>
        /// <param name="r">半径</param>
        public void AddCollision(string id, float x, float y, float r)
        {
            var range = GetRangeByRadius(x, y, r);
            for (int i = range.x, last_i = range.z; i <= last_i; i++)
            {
                for (int j = range.y, last_j = range.w; j <= last_j; j++)
                {
                    Dictionary<int, T> dic;
                    if (!m_CollisionDic.TryGetValue(i, out dic))
                    {
                        dic = new Dictionary<int, T>();
                        m_CollisionDic.Add(i, dic);
                    }

                    if (!dic.ContainsKey(j))
                    {
                        dic.Add(j, OnNewData());
                    }
                    dic[j] = OnAddData(id, dic[j]);
                }
            }
            OnAddDataFinish(id, x, y, r);
        }

        /// <summary>
        /// 以指定坐标为中心，半径所覆盖的区域移除指定碰撞
        /// </summary>
        /// <param name="id">碰撞id</param>
        /// <param name="x">横坐标</param>
        /// <param name="y">纵坐标</param>
        /// <param name="r">半径</param>
        public void RemoveCollision(string id, float x, float y, float r)
        {
            var range = GetRangeByRadius(x, y, r);
            for (int i = range.x, last_i = range.z; i <= last_i; i++)
            {
                for (int j = range.y, last_j = range.w; j <= last_j; j++)
                {
                    Dictionary<int, T> dic;
                    if (m_CollisionDic.TryGetValue(i, out dic))
                    {
                        if (dic.ContainsKey(j))
                        {
                            dic[j] = OnRemoveData(id, dic[j]);
                            //dic.Remove(j);
                        }
                    }
                }
            }
            OnRemoveDataFinish(id, x, y, r);
        }

        /// <summary>
        /// 以指定坐标为中心，半径所覆盖的区域检测是否有碰撞
        /// </summary>
        /// <param name="x">横坐标</param>
        /// <param name="y">纵坐标</param>
        /// <param name="r">半径</param>
        /// <returns>碰撞结果</returns>
        public string IsHit(float x, float y, float r)
        {
            m_TmpList.Clear();
            var range = GetRangeByRadius(x, y, r);
            for (int i = range.x, last_i = range.z; i <= last_i; i++)
            {
                for (int j = range.y, last_j = range.w; j <= last_j; j++)
                {
                    Dictionary<int, T> dic;
                    if (m_CollisionDic.TryGetValue(i, out dic))
                    {
                        T t;
                        if (dic.TryGetValue(j, out t))
                        {
                            m_TmpList.Add(t);
                        }
                    }
                }
            }

            return OnIsHit(x, y, r, m_TmpList);
        }

        /// <summary>
        /// 清空数据
        /// </summary>
        public void Clear()
        {
            OnClear();
            m_CollisionDic.Clear();
            m_TmpList.Clear();
        }

        /// <summary>
        /// 根据中心坐标以及半径，计算覆盖的矩形区域
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="r"></param>
        /// <returns></returns>
        private int4 GetRangeByRadius(float x, float y, float r)
        {
            float halfR = r / 2;
            int leftX = (int)((x - halfR) / m_GridSize);
            int bottomY = (int)((y - halfR) / m_GridSize);
            int rightX = (int)((x + halfR) / m_GridSize);
            int upY = (int)((y + halfR) / m_GridSize);
            return new int4 { x = leftX, y = bottomY, z = rightX, w = upY };
        }

        /// <summary>
        /// 新建数据时响应
        /// </summary>
        /// <returns></returns>
        protected abstract T OnNewData();

        /// <summary>
        /// 添加数据时响应
        /// </summary>
        /// <param name="id"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        protected abstract T OnAddData(string id, T t);

        /// <summary>
        /// 添加数据完成时响应
        /// </summary>
        /// <param name="id"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="r"></param>
        protected abstract void OnAddDataFinish(string id, float x, float y, float r);

        /// <summary>
        /// 移除数据时响应
        /// </summary>
        /// <param name="id"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        protected abstract T OnRemoveData(string id, T t);

        /// <summary>
        /// 移除数据完成时响应
        /// </summary>
        /// <param name="id"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="r"></param>
        protected abstract void OnRemoveDataFinish(string id, float x, float y, float r);

        /// <summary>
        /// 检测是否碰撞时响应
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="r"></param>
        /// <param name="list"></param>
        /// <returns></returns>
        protected abstract string OnIsHit(float x, float y, float r, List<T> list);

        /// <summary>
        /// 清除数据时响应
        /// </summary>
        protected abstract void OnClear();
    }

}
